/**
 * Copyright (C) 2009+ Spark Zheng
 *
 * @file        src/connection.c 
 * @date        2010-04-05
 * @brief	general_network
 * @see         dserver
 * @history
 */

#include <dlib/mempool.h>

#include "dserver_log.h"
#include "dserver_errno.h"
#include "connection.h"
#include "utils.h"

static inline int _conn_add_group(dserver_cpool_t *cpool);

static inline int _conn_add_group(dserver_cpool_t *cpool)
{
	uint32_t i;
	dserver_conn_t *conn = NULL;

	if (cpool->cp_total >= cpool->cp_max) {
		return -E_RES_LIMITED;
	} else {
		for (i = 0; i < CONN_INIT_NUM && cpool->cp_total <= cpool->cp_max; i++) {
			conn = dmalloc_nb(sizeof(*conn));
			if (conn != NULL) {
				dserver_conn_setup(conn);
				cpool->cp_total++;
				cpool->cp_curr++;
				HLIST_ADD(&cpool->c_head, &conn->c_node);
			}
		}
	}

	return RET_SUCCESS;
}

dserver_cpool_t *dserver_cpool_init(uint32_t max_conn)
{
	dserver_cpool_t *cpool = NULL;
	
	if (max_conn < CONN_INIT_NUM)
		max_conn = CONN_INIT_NUM;

	cpool = dmalloc(sizeof(*cpool));
	dlib_lock_init(&cpool->cp_lock);
	HLIST_HEAD_INIT(&cpool->c_head);
	
	cpool->cp_max = max_conn;
	cpool->cp_total = 0;
	cpool->cp_curr = 0;

	dlib_lock_lock(&cpool->cp_lock);
	if (_conn_add_group(cpool) < 0) {
#ifdef DSERVER_NETWORK_NEED_DLOG
		DLOG_ERR(LOG_MAIN, "_conn_add_group error,resource limited.");
#endif
		dlib_lock_unlock(&cpool->cp_lock);
		return NULL;
	}
	dlib_lock_unlock(&cpool->cp_lock);
	return cpool;
}

void dserver_cpool_exit(dserver_cpool_t *cpool)
{
	hlist_node_t *n;
	hlist_node_t *pos;
	dserver_conn_t *tpos;

	if (cpool == NULL) {
		return;
	}
	
	dlib_lock_lock(&cpool->cp_lock);
	HLIST_FOREACH_ENTRY_SAFE(tpos, pos, n, &cpool->c_head, c_node) {
		dserver_conn_destroy(tpos);
		dfree(tpos);
		cpool->cp_curr--;
		cpool->cp_total--;
	}
	dlib_lock_unlock(&cpool->cp_lock);
	dlib_lock_exit(&cpool->cp_lock);
	
	dfree(cpool);
	cpool = NULL;

	return;
}

dserver_conn_t *dserver_conn_fetch(dserver_cpool_t *cpool)
{
	dserver_conn_t *conn = NULL;

	if (cpool == NULL) {
		conn = (dserver_conn_t *)dmalloc_nb(sizeof(*conn));
#ifdef DSERVER_NETWORK_NEED_DLOG
		if (conn == NULL) {
			DLOG_ERR(LOG_MAIN, "dserver_conn_fetch error, resource limited.");
		}
#endif
		return conn;
	}

	dlib_lock_lock(&cpool->cp_lock);
	if (cpool->cp_curr != 0) {
		HLIST_POP_ENTRY(&cpool->c_head, c_node, conn);
		cpool->cp_curr--;
	} else if (cpool->cp_total < cpool->cp_max) {
		if (_conn_add_group(cpool) < 0) {
#ifdef DSERVER_NETWORK_NEED_DLOG
			DLOG_ERR(LOG_MAIN, "dserver_conn_fetch error, resource limited.");
#endif
		} else {
			HLIST_POP_ENTRY(&cpool->c_head, c_node, conn);
			cpool->cp_curr--;
		}
	} 
	dlib_lock_unlock(&cpool->cp_lock);

	if (conn != NULL)
		dserver_conn_cleanup(conn);

	return conn;
}

int dserver_conn_restore(dserver_cpool_t *cpool, dserver_conn_t *conn)
{
	if (conn == NULL) {
		return -E_ARGS_ILLEGAL;
	}

	dserver_conn_cleanup(conn);

	if (cpool == NULL) {
		dfree(conn);
		conn = NULL;
		return RET_SUCCESS;
	}

	dlib_lock_lock(&cpool->cp_lock);
	HLIST_PUSH(&cpool->c_head, &conn->c_node);
	cpool->cp_curr++;
	dlib_lock_unlock(&cpool->cp_lock);

	return RET_SUCCESS;
}
